package com.egg.auth.service.user.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.egg.auth.entity.dept.Dept;
import com.egg.auth.entity.dept.DeptLinkUser;
import com.egg.auth.entity.role.Role;
import com.egg.auth.entity.user.User;
import com.egg.auth.mapper.user.UserMapper;
import com.egg.auth.model.request.web.UserPut;
import com.egg.auth.model.response.web.UserInfo;
import com.egg.auth.model.response.web.UserPage;
import com.egg.auth.service.dept.IDeptLinkUserService;
import com.egg.auth.service.dept.IDeptService;
import com.egg.auth.service.role.IRoleLinkUserService;
import com.egg.auth.service.role.IRoleService;
import com.egg.auth.service.user.IUserAccountService;
import com.egg.auth.service.user.IUserService;
import com.egg.common.mybatis.service.impl.BaseServiceImpl;
import com.egg.common.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * @author east
 */
@Slf4j
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements IUserService {


    @Autowired
    private IUserAccountService userAccountService;
    @Autowired
    private IRoleLinkUserService roleLinkUserService;
    @Autowired
    private IRoleService roleService;
    @Autowired
    private IDeptLinkUserService deptLinkUserService;
    @Autowired
    private IDeptService deptService;


    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void put(Serializable id, UserPut userPut) {
        User user = baseGetById(id);
        User save = JsonUtil.objToObj(userPut, User.class);
        save.setId(user.getId());
        baseSave(save);
        roleLinkUserService.put(user.getId(), userPut.getRoleId());
        deptLinkUserService.put(user.getId(), userPut.getDeptId());
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void delUser(Collection<? extends Serializable> userIds) {
        if (ObjectUtil.isEmpty(userIds)) {
            return;
        }
        roleLinkUserService.delByUserId(userIds);
        deptLinkUserService.delByUserId(userIds);
        userAccountService.delByUserId(userIds);
        baseDel(userIds);
        userIds.forEach(StpUtil::logout);
    }

    @Override
    public Optional<User> findByPhone(String phone) {
        if (ObjectUtil.isEmpty(phone)) {
            return Optional.empty();
        }
        LambdaQueryWrapper<User> qw = buildLambdaSortQw();
        qw.eq(User::getPhone, phone);
        qw.last("LIMIT 1");
        return Optional.ofNullable(getOne(qw));
    }

    @Override
    public Page<User> page(Integer current, Integer pageSize, Optional<String> state, Optional<Boolean> online, Optional<String> search, Optional<Serializable> deptId) {
        return page(
                current, pageSize
                , Optional.empty()
                , Optional.empty()
                , Optional.empty()
                , Optional.empty()
                , state
                , Optional.empty()
                , online
                , search
                , deptId
        );
    }

    @Override
    public Page<User> page(
            Integer current, Integer pageSize
            , Optional<Collection<? extends Serializable>> ids
            , Optional<String> realName
            , Optional<String> nickname
            , Optional<String> phone
            , Optional<String> state
            , Optional<String> gender
            , Optional<Boolean> online
            , Optional<String> search
            , Optional<Serializable> deptId
    ) {
        LambdaQueryWrapper<User> qw = buildLambdaSortQw();
        ids.ifPresent(o -> qw.in(User::getId, o));
        realName.ifPresent(o -> qw.like(User::getRealName, o));
        nickname.ifPresent(o -> qw.like(User::getNickname, o));
        phone.ifPresent(o -> qw.like(User::getPhone, o));
        state.ifPresent(o -> qw.eq(User::getState, o));
        gender.ifPresent(o -> qw.eq(User::getGender, o));
        online.ifPresent(o -> qw.eq(User::getOnlineState, o));
        search.ifPresent(o ->
                qw.and(wrapper ->
                        wrapper.like(User::getRealName, o)
                                .or().like(User::getNickname, o)
                                .or().like(User::getPhone, o)
                )
        );
        if (deptId.isPresent()) {
            String outTableName = getTableName(User.class);
            String inTableName = getTableName(DeptLinkUser.class);
            StringBuilder exist = buildExistsSqlStr(outTableName, "id", inTableName, "user_id");
            exist.append(" AND ").append(inTableName).append(StringPool.DOT).append("dept_id").append(StringPool.EQUALS).append(deptId.get());
            qw.exists(exist.toString());
        }
        return page(new Page<>(current, pageSize), qw);
    }

    @Override
    public List<UserPage> convertUserPage(List<User> sourceUserList) {
        if (ObjectUtil.isEmpty(sourceUserList)) {
            return Collections.emptyList();
        }
        List<Long> userIds = sourceUserList.stream().map(o -> o.getId()).collect(Collectors.toList());
        List<DeptLinkUser> deptLinkUserList = deptLinkUserService.findByUserId(userIds);
        Map<Long, List<Long>> deptUserMap = deptLinkUserList.stream().collect(Collectors.groupingBy(o -> o.getUserId(), Collectors.mapping(o -> o.getDeptId(), Collectors.toList())));
        List<Dept> deptList = deptService.baseFindById(deptLinkUserList.stream().map(o -> o.getDeptId()).collect(Collectors.toSet()));
        List<UserPage> userList = new ArrayList<>();
        sourceUserList.forEach(user -> {
            UserPage temp = JsonUtil.objToObj(user, UserPage.class);
            userList.add(temp);
            List<Long> deptId = deptUserMap.getOrDefault(user.getId(), Collections.emptyList());
            List<Dept> dept = deptList.stream().filter(o -> deptId.contains(o.getId())).collect(Collectors.toList());
            temp.setDeptName(dept.stream().map(o -> o.getName()).collect(Collectors.joining(",")));
        });
        return userList;
    }

    @Override
    public void resetOnline(Serializable userId) {
        User temp = new User();
        temp.setId(Long.parseLong(userId.toString()));
        temp.setOnlineState(true);
        temp.setLastLoginDate(LocalDateTime.now());
        baseEdit(temp);
    }

    @Override
    public void resetOffline(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setOnlineState(false);
        baseEdit(user);
    }

    @Override
    public UserInfo buildUserInfoByUserId(Serializable userId) {
        User user = baseGetById(userId);
        UserInfo userInfo = JsonUtil.objToObj(user, UserInfo.class);
        List<Role> role = roleService.findByUserId(userId);
        userInfo.setRoleId(role.stream().map(o -> o.getId()).collect(Collectors.toList()));
        List<Dept> dept = deptService.findByUserId(userId);
        userInfo.setDeptId(dept.stream().map(o -> o.getId()).collect(Collectors.toList()));
        return userInfo;
    }


}
